package com.danbay.framework.kafka.coap;

import com.danbay.framework.utils.ByteUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.core.coap.CoAP;
import org.eclipse.californium.core.coap.OptionSet;
import org.eclipse.californium.core.coap.Request;
import org.eclipse.californium.core.coap.Response;

import java.util.HashMap;
import java.util.Map;

/**
 * CoAP包
 *
 * @author SJG
 *         2017/11/14.
 */
@Slf4j
public class CoAPPackageBuilder {

    /**
     * 生成CoAP包
     *
     * @param path 命令url
     * @param data 数据
     * @param <T>  数据类型
     * @return CoAP包
     */
    public static <T> Response buildPackage(String path, T data) {
        String jsonData = "";
        if (data != null) {
            try {
                jsonData = new ObjectMapper().writeValueAsString(data);
            } catch (JsonProcessingException e) {
                log.error("json CoAP data failed", e);
            }
        }
        log.info("build CoAP request package...data:{}", jsonData);
        Response response = buildResponse(
                CoAP.ResponseCode.CONTINUE,
                path,
                CoAPMID.getNextMID(),
                CoAPToken.getNextToken(),
                jsonData
        );
        return response;
    }

    /**
     * 生成request CoAP包
     *
     * @param code @see CoAP.Code
     * @param type @see CoAP.Type
     * @param path 命令url
     * @param data 数据
     * @param <T>  数据类型
     * @return CoAP包
     */
    public static <T> Request buildPackage(CoAP.Code code, CoAP.Type type, String path, T data) {
        String jsonData = "";
        if (data != null) {
            try {
                jsonData = new ObjectMapper().writeValueAsString(data);
            } catch (JsonProcessingException e) {
                log.error("json CoAP data failed", e);
            }
        }
        log.info("build CoAP request package...data:{}", jsonData);
        return buildRequest(
                code,
                type,
                path,
                CoAPMID.getNextMID(),
                null,
                jsonData
        );
    }


    /**
     * 生成response CoAP包
     *
     * @param code @see CoAP.Code
     * @param path 命令url
     * @param data 数据
     * @param <T>  数据类型
     * @return CoAP包
     */
    public static <T> Response buildPackage(CoAP.ResponseCode code, String path, int srcMid, Long srcToken, T data) {
        String jsonData = "";
        if (data != null) {
            if (data instanceof String) {
                jsonData = data.toString();
            } else {
                try {
                    jsonData = new ObjectMapper().writeValueAsString(data);
                } catch (JsonProcessingException e) {
                    log.error("json CoAP data failed", e);
                }
            }
        }
        log.info("build CoAP request package...data:{}", jsonData);
        return buildResponse(
                code,
                path,
                srcMid,
                srcToken,
                jsonData
        );
    }


    /**
     * 生成ACK的CoAP包
     *
     * @param path 命令url
     * @return CoAP包
     */
    public static Response buildACKPackage(String path) {
        return buildResponse(
                CoAP.ResponseCode.CREATED,
                path,
                CoAPMID.getNextMID(),
                CoAPToken.getNextToken(),
                "ACK"
        );
    }

    /**
     * 生成ACK的CoAP包
     *
     * @param path     命令url
     * @param srcMid   源mid
     * @param srcToken 源token
     * @param data     数据
     * @return CoAP包
     */
    public static <T> Response buildACKDataPackage(String path, int srcMid, Long srcToken, T data) {
        CoAP.ResponseCode code;
        //由于接口文档中返回code类型不统一，需要单独硬编码处理
        if (path.startsWith("/device/login")) {
            code = CoAP.ResponseCode.VALID;
        } else if (path.startsWith("/device/register")) {
            code = CoAP.ResponseCode.CREATED;
        } else {
            code = CoAP.ResponseCode.CONTENT;
        }

        return buildPackage(code, path, srcMid, srcToken, data);
    }

    /**
     * 生成ACK的错误响应CoAP包
     *
     * @param path     命令url
     * @param srcMid   源mid
     * @param srcToken 源token
     * @return CoAP包
     */
    public static Response buildACKBadRequestPackage(String path, int srcMid, Long srcToken) {
        return buildPackage(CoAP.ResponseCode.BAD_REQUEST, path, srcMid, srcToken, null);
    }

    /**
     * 生成需要对方回应的POST类型的CoAP包
     *
     * @param path 命令url
     * @param data 数据
     * @param <T>  数据类型
     * @return CoAP包
     */
    public static <T> Request buildPostCONPackage(String path, T data) {
        return buildPackage(CoAP.Code.POST, CoAP.Type.CON, path, data);
    }

    /**
     * 生成不需要对方回应的POST类型的CoAP包
     *
     * @param path 命令url
     * @param data 数据
     * @param <T>  数据类型
     * @return CoAP包
     */
    public static <T> Request buildPostNONPackage(String path, T data) {
        return buildPackage(CoAP.Code.POST, CoAP.Type.NON, path, data);
    }

    /**
     * 生成需要对方回应的GET类型的CoAP包
     *
     * @param path 命令url
     * @param data 数据
     * @param <T>  数据类型
     * @return CoAP包
     */
    public static <T> Request buildGetCONPackage(String path, T data) {
        return buildPackage(CoAP.Code.GET, CoAP.Type.CON, path, data);
    }

    /**
     * 生成CoAP请求对象
     *
     * @param code    @see CoAP.Code
     * @param type    @see CoAP.Type
     * @param uri     命令uri
     * @param mid     消息id
     * @param token   token
     * @param payload 数据
     * @return CoAP请求对象
     */
    public static Request buildRequest(CoAP.Code code, CoAP.Type type, String uri, int mid, Long token, String payload) {
        Request request = new Request(code, type);
        request.setMID(mid);
        if (token != null) {
            //token作为事务功能，无事务可不用token
            request.setToken(ByteUtils.long2Bytes(token));
        } else {
            request.setToken(new byte[0]);
        }
        request.setOptions(buildOptions(uri));
        request.setPayload(payload);
        return request;
    }

    /**
     * 生成CoAP响应对象
     *
     * @param code    @see CoAP.Code
     * @param uri     命令uri
     * @param mid     消息id
     * @param token   token
     * @param payload 数据
     * @return CoAP请求对象
     */
    public static Response buildResponse(CoAP.ResponseCode code, String uri, int mid, Long token, String payload) {
        Response response = new Response(code);
        response.setType(CoAP.Type.ACK);
        response.setMID(mid);
        if (token != null) {
            //token作为事务功能，无事务可不用token
            response.setToken(ByteUtils.long2Bytes(token));
        } else {
            response.setToken(new byte[0]);
        }
        response.setOptions(buildOptions(uri));
        response.setPayload(payload);
        return response;
    }

    /**
     * 生成OptionSet
     *
     * @param uri uri
     * @return OptionSet
     */
    private static OptionSet buildOptions(String uri) {
        OptionSet optionSet = new OptionSet();
        String uriQuery = "";
        int q = uri.indexOf("?");
        if (q > 0) {
            uriQuery = uri.substring(q) + "&";
            uri = uri.substring(0, q);
        }
        optionSet.setUriPath(uri);
        optionSet.setUriQuery(uriQuery);
        return optionSet;
    }
}
